<html>
<head>
  <title>Test for contenteditable focus</title>
  <script type="text/javascript"
          src="/tests/SimpleTest/SimpleTest.js"></script>
  <link rel="stylesheet" type="text/css"
          href="/tests/SimpleTest/test.css" />
</head>
<body>
<div id="display">
  First text in this document.<br>
  <input id="inputText" type="text"><br>
  <input id="inputTextReadonly" type="text" readonly><br>
  <input id="inputButton" type="button" value="input[type=button]"><br>
  <button id="button">button</button><br>
  <div id="editor" contenteditable="true">
    editable contents.<br>
    <input id="inputTextInEditor" type="text"><br>
    <input id="inputTextReadonlyInEditor" type="text" readonly><br>
    <input id="inputButtonInEditor" type="button" value="input[type=button]"><br>
    <button id="buttonInEditor">button</button><br>
    <div id="noeditableInEditor" contenteditable="false">
      <span id="spanInNoneditableInEditor">span element in noneditable in editor</span><br>
      <input id="inputTextInNoneditableInEditor" type="text"><br>
      <input id="inputTextReadonlyInNoneditableInEditor" type="text" readonly><br>
      <input id="inputButtonInNoneditableInEditor" type="button" value="input[type=button]"><br>
      <button id="buttonInNoneditableInEditor">button</button><br>
    </div>
    <span id="spanInEditor">span element in editor</span><br>
  </div>
  <div id="otherEditor" contenteditable="true">
    other editor.
  </div>
</div>
<div id="content" style="display: none">
  
</div>
<pre id="test">
</pre>

<script class="testbody" type="application/javascript">

SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(runTests, window);

function runTests()
{
  runTestsInternal();
  SimpleTest.finish();
}

function runTestsInternal()
{
  var fm = SpecialPowers.Cc["@mozilla.org/focus-manager;1"].
             getService(SpecialPowers.Ci.nsIFocusManager);
  // XXX using selCon for checking the visibility of the caret, however,
  // selCon is shared in document, cannot get the element of owner of the
  // caret from javascript?
  var selCon = SpecialPowers.wrap(window).
        QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor).
        getInterface(SpecialPowers.Ci.nsIWebNavigation).
        QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor).
        getInterface(SpecialPowers.Ci.nsISelectionDisplay).
        QueryInterface(SpecialPowers.Ci.nsISelectionController);
  var selection = window.getSelection();

  var inputText = document.getElementById("inputText");
  var inputTextReadonly = document.getElementById("inputTextReadonly");
  var inputButton = document.getElementById("inputButton");
  var button = document.getElementById("button");
  var editor = document.getElementById("editor");
  var inputTextInEditor = document.getElementById("inputTextInEditor");
  var inputTextReadonlyInEditor = document.getElementById("inputTextReadonlyInEditor");
  var inputButtonInEditor = document.getElementById("inputButtonInEditor");
  var noeditableInEditor = document.getElementById("noeditableInEditor");
  var spanInNoneditableInEditor = document.getElementById("spanInNoneditableInEditor");
  var inputTextInNoneditableInEditor = document.getElementById("inputTextInNoneditableInEditor");
  var inputTextReadonlyInNoneditableInEditor = document.getElementById("inputTextReadonlyInNoneditableInEditor");
  var inputButtonInNoneditableInEditor = document.getElementById("inputButtonInNoneditableInEditor");
  var buttonInNoneditableInEditor = document.getElementById("buttonInNoneditableInEditor");
  var spanInEditor = document.getElementById("spanInEditor");
  var otherEditor = document.getElementById("otherEditor");

  // XXX if there is a contenteditable element, HTML editor sets dom selection
  // to first editable node, but this makes inconsistency with normal document
  // behavior.
  todo_is(selection.rangeCount, 0, "unexpected selection range is there");
  ok(!selCon.caretVisible, "caret is visible in the document");
  // Move focus to inputTextInEditor
  inputTextInEditor.focus();
  is(SpecialPowers.unwrap(fm.focusedElement), inputTextInEditor,
     "inputTextInEditor didn't get focus");
  todo_is(selection.rangeCount, 0, "unexpected selection range is there");
  ok(selCon.caretVisible, "caret isn't visible in the inputTextInEditor");
  // Move focus to the editor
  editor.focus();
  is(SpecialPowers.unwrap(fm.focusedElement), editor,
     "editor didn't get focus");
  is(selection.rangeCount, 1,
     "there is no selection range when editor has focus");
  var range = selection.getRangeAt(0);
  ok(range.collapsed, "the selection range isn't collapsed");
  var startNode = range.startContainer;
  is(startNode.nodeType, 1, "the caret isn't set to the div node");
  is(startNode, editor, "the caret isn't set to the editor");
  ok(selCon.caretVisible, "caret isn't visible in the editor");
  // Move focus to other editor
  otherEditor.focus();
  is(SpecialPowers.unwrap(fm.focusedElement), otherEditor,
     "the other editor didn't get focus");
  is(selection.rangeCount, 1,
     "there is no selection range when the other editor has focus");
  range = selection.getRangeAt(0);
  ok(range.collapsed, "the selection range isn't collapsed");
  var startNode = range.startContainer;
  is(startNode.nodeType, 1, "the caret isn't set to the div node");
  is(startNode, otherEditor, "the caret isn't set to the other editor");
  ok(selCon.caretVisible, "caret isn't visible in the other editor");
  // Move focus to inputTextInEditor
  inputTextInEditor.focus();
  is(SpecialPowers.unwrap(fm.focusedElement), inputTextInEditor,
     "inputTextInEditor didn't get focus #2");
  is(selection.rangeCount, 1, "selection range is lost from the document");
  range = selection.getRangeAt(0);
  ok(range.collapsed, "the selection range isn't collapsed");
  var startNode = range.startContainer;
  is(startNode.nodeType, 1, "the caret isn't set to the div node");
  // XXX maybe, the caret can stay on the other editor if it's better.
  is(startNode, editor,
     "the caret should stay on the other editor");
  ok(selCon.caretVisible,
     "caret isn't visible in the inputTextInEditor");
  // Move focus to the other editor again
  otherEditor.focus();
  is(SpecialPowers.unwrap(fm.focusedElement), otherEditor,
     "the other editor didn't get focus #2");
  // Set selection to the span element in the editor (unfocused)
  range = document.createRange();
  range.setStart(spanInEditor.firstChild, 5);
  selection.removeAllRanges();
  selection.addRange(range);
  is(selection.rangeCount, 1, "selection range is lost from the document");
  is(SpecialPowers.unwrap(fm.focusedElement), otherEditor,
     "the other editor shouldn't lose focus by selection range change");
  ok(selCon.caretVisible, "caret isn't visible in inputTextInEditor");
  // Move focus to the editor
  editor.focus();
  is(SpecialPowers.unwrap(fm.focusedElement), editor,
     "the editor didn't get focus #2");
  is(selection.rangeCount, 1, "selection range is lost from the document");
  range = selection.getRangeAt(0);
  ok(range.collapsed, "the selection range isn't collapsed");
  is(range.startOffset, 5,
     "the caret is moved when the editor was focused (offset)");
  var startNode = range.startContainer;
  is(startNode.nodeType, 3, "the caret isn't in text node");
  is(startNode.parentNode, spanInEditor,
     "the caret is moved when the editor was focused (node)");
  ok(selCon.caretVisible, "caret isn't visible in the editor (spanInEditor)");

  // Move focus to each focusable element in the editor.
  function testFocusMove(aSetFocusElementID, aFocusable, aCaretVisible)
  {
    editor.focus();
    is(SpecialPowers.unwrap(fm.focusedElement), editor,
       "testFocusMove: the editor didn't get focus at initializing (" +
       aSetFocusElementID + ")");
    var setFocusElement = document.getElementById(aSetFocusElementID);
    setFocusElement.focus();
    if (aFocusable) {
      is(SpecialPowers.unwrap(fm.focusedElement), setFocusElement,
         "testFocusMove: the " + aSetFocusElementID +
         " didn't get focus");
    } else {
      is(SpecialPowers.unwrap(fm.focusedElement), editor,
         "testFocusMove: the editor lost focus by focus() of the " +
         aSetFocusElementID);
    }
    if (aCaretVisible) {
      ok(selCon.caretVisible,
         "testFocusMove: caret isn't visible when the " +
         aSetFocusElementID + " has focus");
    } else {
      ok(!selCon.caretVisible,
         "testFocusMove: caret is visible when the " +
         aSetFocusElementID + " has focus");
    }
  }
  testFocusMove("inputTextInEditor", true, true);
  testFocusMove("inputTextReadonlyInEditor", true, true);
  // XXX shouldn't the caret become invisible?
  testFocusMove("inputButtonInEditor", true, true);
  testFocusMove("noeditableInEditor", false, true);
  testFocusMove("spanInNoneditableInEditor", false, true);
  testFocusMove("inputTextInNoneditableInEditor", true, true);
  testFocusMove("inputTextReadonlyInNoneditableInEditor", true, true);
  testFocusMove("inputButtonInNoneditableInEditor", true, false);
  testFocusMove("buttonInNoneditableInEditor", true, false);
  testFocusMove("spanInEditor", false, true);
  testFocusMove("inputText", true, true);
  testFocusMove("inputTextReadonly", true, true);
  testFocusMove("inputButton", true, false);
  testFocusMove("button", true, false);
}

</script>
</body>

</html>
